//===--- TypeCheckAvailability.cpp - Availability Diagnostics -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements availability diagnostics.
//
//===----------------------------------------------------------------------===//

#include "polarphp/sema/internal/TypeCheckAvailability.h"
#include "polarphp/sema/internal/TypeChecker.h"
#include "polarphp/sema/internal/MiscDiagnostics.h"
#include "polarphp/ast/AstWalker.h"
#include "polarphp/ast/Initializer.h"
#include "polarphp/ast/NameLookup.h"
#include "polarphp/ast/Pattern.h"
#include "polarphp/ast/SourceFile.h"
#include "polarphp/ast/TypeRefinementContext.h"
#include "polarphp/basic/SourceMgr.h"
#include "polarphp/basic/StringExtras.h"
#include "polarphp/llparser/Lexer.h"
#include "polarphp/llparser/Parser.h"
#include "polarphp/sema/IDETypeChecking.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace polar;
using namespace polar::llparser;

/// Returns the first availability attribute on the declaration that is active
/// on the target platform.
static const AvailableAttr *getActiveAvailableAttribute(const Decl *D,
                                                        AstContext &AC) {
   for (auto Attr : D->getAttrs())
      if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
         if (!AvAttr->isInvalid() && AvAttr->isActivePlatform(AC)) {
            return AvAttr;
         }
      }
   return nullptr;
}

/// Returns true if there is any availability attribute on the declaration
/// that is active on the target platform.
static bool hasActiveAvailableAttribute(Decl *D,
                                        AstContext &AC) {
   return getActiveAvailableAttribute(D, AC);
}

namespace {

/// A class to walk the AST to build the type refinement context hierarchy.
class TypeRefinementContextBuilder : private AstWalker {

   struct ContextInfo {
      TypeRefinementContext *TRC;

      /// The node whose end marks the end of the refinement context.
      /// If the builder sees this node in a post-visitor, it will pop
      /// the context from the stack. This node can be null (ParentTy()),
      /// indicating that custom logic elsewhere will handle removing
      /// the context when needed.
      ParentTy ScopeNode;
   };

   std::vector<ContextInfo> ContextStack;
   AstContext &Context;

   /// A mapping from abstract storage declarations with accessors to
   /// to the type refinement contexts for those declarations. We refer to
   /// this map to determine the appropriate parent TRC to use when
   /// walking the accessor function.
   llvm::DenseMap<AbstractStorageDecl *, TypeRefinementContext *>
      StorageContexts;

   TypeRefinementContext *getCurrentTRC() {
      return ContextStack.back().TRC;
   }

   void pushContext(TypeRefinementContext *TRC, ParentTy PopAfterNode) {
      ContextInfo Info;
      Info.TRC = TRC;
      Info.ScopeNode = PopAfterNode;
      ContextStack.push_back(Info);
   }

public:
   TypeRefinementContextBuilder(TypeRefinementContext *TRC, AstContext &Context)
      : Context(Context) {
      assert(TRC);
      pushContext(TRC, ParentTy());
   }

   void build(Decl *D) {
      unsigned StackHeight = ContextStack.size();
      D->walk(*this);
      assert(ContextStack.size() == StackHeight);
      (void)StackHeight;
   }

   void build(Stmt *S) {
      unsigned StackHeight = ContextStack.size();
      S->walk(*this);
      assert(ContextStack.size() == StackHeight);
      (void)StackHeight;
   }

   void build(Expr *E) {
      unsigned StackHeight = ContextStack.size();
      E->walk(*this);
      assert(ContextStack.size() == StackHeight);
      (void)StackHeight;
   }

private:
   bool walkToDeclPre(Decl *D) override {
      TypeRefinementContext *DeclTRC = getNewContextForWalkOfDecl(D);

      if (DeclTRC) {
         pushContext(DeclTRC, D);
      }

      return true;
   }

   bool walkToDeclPost(Decl *D) override {
      if (ContextStack.back().ScopeNode.getAsDecl() == D) {
         ContextStack.pop_back();
      }
      return true;
   }

   /// Returns a new context to be introduced for the declaration, or nullptr
   /// if no new context should be introduced.
   TypeRefinementContext *getNewContextForWalkOfDecl(Decl *D) {
      if (auto accessor = dyn_cast<AccessorDecl>(D)) {
         // Use TRC of the storage rather the current TRC when walking this
         // function.
         auto it = StorageContexts.find(accessor->getStorage());
         if (it != StorageContexts.end()) {
            return it->second;
         }
      }

      if (declarationIntroducesNewContext(D)) {
         return buildDeclarationRefinementContext(D);
      }

      return nullptr;
   }

   /// Builds the type refinement hierarchy for the body of the function.
   TypeRefinementContext *buildDeclarationRefinementContext(Decl *D) {
      // We require a valid range in order to be able to query for the TRC
      // corresponding to a given SourceLoc.
      // If this assert fires, it means we have probably synthesized an implicit
      // declaration without location information. The appropriate fix is
      // probably to gin up a source range for the declaration when synthesizing
      // it.
      assert(D->getSourceRange().isValid());

      // The potential versions in the declaration are constrained by both
      // the declared availability of the declaration and the potential versions
      // of its lexical context.
      AvailabilityContext DeclInfo =
         polar::AvailabilityInference::availableRange(D, Context);
      DeclInfo.intersectWith(getCurrentTRC()->getAvailabilityInfo());

      TypeRefinementContext *NewTRC =
         TypeRefinementContext::createForDecl(Context, D, getCurrentTRC(),
                                              DeclInfo,
                                              refinementSourceRangeForDecl(D));

      // Record the TRC for this storage declaration so that
      // when we process the accessor, we can use this TRC as the
      // parent.
      if (auto *StorageDecl = dyn_cast<AbstractStorageDecl>(D)) {
         if (StorageDecl->hasParsedAccessors()) {
            StorageContexts[StorageDecl] = NewTRC;
         }
      }

      return NewTRC;
   }

   /// Returns true if the declaration should introduce a new refinement context.
   bool declarationIntroducesNewContext(Decl *D) {
      if (!isa<ValueDecl>(D) && !isa<ExtensionDecl>(D)) {
         return false;
      }

      // No need to introduce a context if the declaration does not have an
      // availability attribute.
      if (!hasActiveAvailableAttribute(D, Context)) {
         return false;
      }

      // Only introduce for an AbstractStorageDecl if it is not local.
      // We introduce for the non-local case because these may
      // have getters and setters (and these may be synthesized, so they might
      // not even exist yet).
      if (auto *storageDecl = dyn_cast<AbstractStorageDecl>(D)) {
         if (storageDecl->getDeclContext()->isLocalContext()) {
            // No need to
            return false;
         }
      }

      return true;
   }

   /// Returns the source range which should be refined by declaration. This
   /// provides a convenient place to specify the refined range when it is
   /// different than the declaration's source range.
   SourceRange refinementSourceRangeForDecl(Decl *D) {
      if (auto *storageDecl = dyn_cast<AbstractStorageDecl>(D)) {
         // Use the declaration's availability for the context when checking
         // the bodies of its accessors.

         // HACK: For synthesized trivial accessors we may have not a valid
         // location for the end of the braces, so in that case we will fall back
         // to using the range for the storage declaration. The right fix here is
         // to update AbstractStorageDecl::addTrivialAccessors() to take brace
         // locations and have callers of that method provide appropriate source
         // locations.
         SourceLoc BracesEnd = storageDecl->getBracesRange().end;
         if (storageDecl->hasParsedAccessors() && BracesEnd.isValid()) {
            return SourceRange(storageDecl->getStartLoc(),
                               BracesEnd);
         }

         // For a variable declaration (without accessors) we use the range of the
         // containing pattern binding declaration to make sure that we include
         // any type annotation in the type refinement context range.
         if (auto varDecl = dyn_cast<VarDecl>(storageDecl)) {
            auto *PBD = varDecl->getParentPatternBinding();
            if (PBD)
               return PBD->getSourceRange();
         }
      }

      return D->getSourceRange();
   }

   std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
      if (auto *IS = dyn_cast<IfStmt>(S)) {
         buildIfStmtRefinementContext(IS);
         return std::make_pair(false, S);
      }

      if (auto *RS = dyn_cast<GuardStmt>(S)) {
         buildGuardStmtRefinementContext(RS);
         return std::make_pair(false, S);
      }

      if (auto *WS = dyn_cast<WhileStmt>(S)) {
         buildWhileStmtRefinementContext(WS);
         return std::make_pair(false, S);
      }

      return std::make_pair(true, S);
   }

   Stmt *walkToStmtPost(Stmt *S) override {
      // If we have multiple guard statements in the same block
      // then we may have multiple refinement contexts to pop
      // after walking that block.
      while (!ContextStack.empty() &&
             ContextStack.back().ScopeNode.getAsStmt() == S) {
         ContextStack.pop_back();
      }

      return S;
   }

   /// Builds the type refinement hierarchy for the IfStmt if the guard
   /// introduces a new refinement context for the Then branch.
   /// There is no need for the caller to explicitly traverse the children
   /// of this node.
   void buildIfStmtRefinementContext(IfStmt *IS) {
      Optional<AvailabilityContext> ThenRange;
      Optional<AvailabilityContext> ElseRange;
      std::tie(ThenRange, ElseRange) =
         buildStmtConditionRefinementContext(IS->getCond());

      if (ThenRange.hasValue()) {
         // Create a new context for the Then branch and traverse it in that new
         // context.
         auto *ThenTRC =
            TypeRefinementContext::createForIfStmtThen(Context, IS,
                                                       getCurrentTRC(),
                                                       ThenRange.getValue());
         TypeRefinementContextBuilder(ThenTRC, Context).build(IS->getThenStmt());
      } else {
         build(IS->getThenStmt());
      }

      Stmt *ElseStmt = IS->getElseStmt();
      if (!ElseStmt)
         return;

      // Refine the else branch if we're given a version range for that branch.
      // For now, if present, this will only be the empty range, indicating
      // that the branch is dead. We use it to suppress potential unavailability
      // and deprecation diagnostics on code that definitely will not run with
      // the current platform and minimum deployment target.
      // If we add a more precise version range lattice (i.e., one that can
      // support "<") we should create non-empty contexts for the Else branch.
      if (ElseRange.hasValue()) {
         // Create a new context for the Then branch and traverse it in that new
         // context.
         auto *ElseTRC =
            TypeRefinementContext::createForIfStmtElse(Context, IS,
                                                       getCurrentTRC(),
                                                       ElseRange.getValue());
         TypeRefinementContextBuilder(ElseTRC, Context).build(ElseStmt);
      } else {
         build(IS->getElseStmt());
      }
   }

   /// Builds the type refinement hierarchy for the WhileStmt if the guard
   /// introduces a new refinement context for the body branch.
   /// There is no need for the caller to explicitly traverse the children
   /// of this node.
   void buildWhileStmtRefinementContext(WhileStmt *WS) {
      Optional<AvailabilityContext> BodyRange =
         buildStmtConditionRefinementContext(WS->getCond()).first;

      if (BodyRange.hasValue()) {
         // Create a new context for the body and traverse it in the new
         // context.
         auto *BodyTRC = TypeRefinementContext::createForWhileStmtBody(
            Context, WS, getCurrentTRC(), BodyRange.getValue());
         TypeRefinementContextBuilder(BodyTRC, Context).build(WS->getBody());
      } else {
         build(WS->getBody());
      }
   }

   /// Builds the type refinement hierarchy for the GuardStmt and pushes
   /// the fallthrough context onto the context stack so that subsequent
   /// AST elements in the same scope are analyzed in the context of the
   /// fallthrough TRC.
   void buildGuardStmtRefinementContext(GuardStmt *GS) {
      // 'guard' statements fall through if all of the
      // guard conditions are true, so we refine the range after the require
      // until the end of the enclosing block.
      // if ... {
      //   guard available(...) else { return } <-- Refined range starts here
      //   ...
      // } <-- Refined range ends here
      //
      // This is slightly tricky because, unlike our other control constructs,
      // the refined region is not lexically contained inside the construct
      // introducing the refinement context.
      Optional<AvailabilityContext> FallthroughRange;
      Optional<AvailabilityContext> ElseRange;
      std::tie(FallthroughRange, ElseRange) =
         buildStmtConditionRefinementContext(GS->getCond());

      if (Stmt *ElseBody = GS->getBody()) {
         if (ElseRange.hasValue()) {
            auto *TrueTRC = TypeRefinementContext::createForGuardStmtElse(
               Context, GS, getCurrentTRC(), ElseRange.getValue());

            TypeRefinementContextBuilder(TrueTRC, Context).build(ElseBody);
         } else {
            build(ElseBody);
         }
      }

      auto *ParentBrace = dyn_cast<BraceStmt>(Parent.getAsStmt());
      assert(ParentBrace && "Expected parent of GuardStmt to be BraceStmt");
      if (!FallthroughRange.hasValue())
         return;

      // Create a new context for the fallthrough.

      auto *FallthroughTRC =
         TypeRefinementContext::createForGuardStmtFallthrough(Context, GS,
                                                              ParentBrace, getCurrentTRC(), FallthroughRange.getValue());

      pushContext(FallthroughTRC, ParentBrace);
   }

   /// Build the type refinement context for a StmtCondition and return a pair
   /// of optional version ranges, the first for the true branch and the second
   /// for the false branch. A value of None for a given branch indicates that
   /// the branch does not introduce a new refinement.
   std::pair<Optional<AvailabilityContext>, Optional<AvailabilityContext>>
   buildStmtConditionRefinementContext(StmtCondition Cond) {

      // Any refinement contexts introduced in the statement condition
      // will end at the end of the last condition element.
      StmtConditionElement LastElement = Cond.back();

      // Keep track of how many nested refinement contexts we have pushed on
      // the context stack so we can pop them when we're done building the
      // context for the StmtCondition.
      unsigned NestedCount = 0;

      // Tracks the potential version range when the condition is false.
      auto FalseFlow = AvailabilityContext::neverAvailable();

      TypeRefinementContext *StartingTRC = getCurrentTRC();

      for (StmtConditionElement Element : Cond) {
         TypeRefinementContext *CurrentTRC = getCurrentTRC();
         AvailabilityContext CurrentInfo = CurrentTRC->getAvailabilityInfo();

         // If the element is not a condition, walk it in the current TRC.
         if (Element.getKind() != StmtConditionElement::CK_Availability) {

            // Assume any condition element that is not a #available() can
            // potentially be false, so conservatively combine the version
            // range of the current context with the accumulated false flow
            // of all other conjuncts.
            FalseFlow.unionWith(CurrentInfo);

            Element.walk(*this);
            continue;
         }

         // #available query: introduce a new refinement context for the statement
         // condition elements following it.
         auto *Query = Element.getAvailability();

         // If this query expression has no queries, we will not introduce a new
         // refinement context. We do not diagnose here: a diagnostic will already
         // have been emitted by the parser.
         if (Query->getQueries().empty())
            continue;

         AvailabilitySpec *Spec = bestActiveSpecForQuery(Query);
         if (!Spec) {
            // We couldn't find an appropriate spec for the current platform,
            // so rather than refining, emit a diagnostic and just use the current
            // TRC.
            Context.Diags.diagnose(
               Query->getLoc(),
               diag::availability_query_required_for_platform,
               platformString(targetPlatform(Context.LangOpts)));

            continue;
         }

         AvailabilityContext NewConstraint = contextForSpec(Spec);
         Query->setAvailableRange(NewConstraint.getOSVersion());

         if (Spec->getKind() == AvailabilitySpecKind::OtherPlatform) {
            // The wildcard spec '*' represents the minimum deployment target, so
            // there is no need to create a refinement context for this query.
            // Further, we won't diagnose for useless #available() conditions
            // where * matched on this platform -- presumably those conditions are
            // needed for some other platform.
            continue;
         }


         // If the version range for the current TRC is completely contained in
         // the range for the spec, then a version query can never be false, so the
         // spec is useless. If so, report this.
         if (CurrentInfo.isContainedIn(NewConstraint)) {
            DiagnosticEngine &Diags = Context.Diags;
            // Some availability checks will always pass because the minimum
            // deployment target guarantees they will never be false. We don't
            // diagnose these checks as useless because the source file may
            // be shared with other projects/targets having older deployment
            // targets. We don't currently have a mechanism for the user to
            // suppress these warnings (for example, by indicating when the
            // required compatibility version is different than the deployment
            // target).
            if (CurrentTRC->getReason() != TypeRefinementContext::Reason::Root) {
               Diags.diagnose(Query->getLoc(),
                              diag::availability_query_useless_enclosing_scope,
                              platformString(targetPlatform(Context.LangOpts)));
               Diags.diagnose(CurrentTRC->getIntroductionLoc(),
                              diag::availability_query_useless_enclosing_scope_here);
            }

            // No need to actually create the refinement context if we know it is
            // useless.
            continue;
         }

         // If the #available() is not useless then there is potential false flow,
         // so join the false flow with the potential versions of the current
         // context.
         // We could be more precise here if we enriched the lattice to include
         // ranges of the form [x, y).
         FalseFlow.unionWith(CurrentInfo);

         auto *TRC = TypeRefinementContext::createForConditionFollowingQuery(
            Context, Query, LastElement, CurrentTRC, NewConstraint);

         pushContext(TRC, ParentTy());
         NestedCount++;
      }


      Optional<AvailabilityContext> FalseRefinement = None;
      // The version range for the false branch should never have any versions
      // that weren't possible when the condition started evaluating.
      assert(FalseFlow.isContainedIn(StartingTRC->getAvailabilityInfo()));

      // If the starting version range is not completely contained in the
      // false flow version range then it must be the case that false flow range
      // is strictly smaller than the starting range (because the false flow
      // range *is* contained in the starting range), so we should introduce a
      // new refinement for the false flow.
      if (!StartingTRC->getAvailabilityInfo().isContainedIn(FalseFlow)) {
         FalseRefinement = FalseFlow;
      }

      if (NestedCount == 0)
         return std::make_pair(None, FalseRefinement);

      TypeRefinementContext *NestedTRC = getCurrentTRC();
      while (NestedCount-- > 0)
         ContextStack.pop_back();

      assert(getCurrentTRC() == StartingTRC);

      return std::make_pair(NestedTRC->getAvailabilityInfo(), FalseRefinement);
   }

   /// Return the best active spec for the target platform or nullptr if no
   /// such spec exists.
   AvailabilitySpec *bestActiveSpecForQuery(PoundAvailableInfo *available) {
      OtherPlatformAvailabilitySpec *FoundOtherSpec = nullptr;
      for (auto *Spec : available->getQueries()) {
         if (auto *OtherSpec = dyn_cast<OtherPlatformAvailabilitySpec>(Spec)) {
            FoundOtherSpec = OtherSpec;
            continue;
         }

         auto *VersionSpec = dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec);
         if (!VersionSpec)
            continue;

         // FIXME: This is not quite right: we want to handle AppExtensions
         // properly. For example, on the OSXApplicationExtension platform
         // we want to chose the OS X spec unless there is an explicit
         // OSXApplicationExtension spec.
         if (isPlatformActive(VersionSpec->getPlatform(), Context.LangOpts)) {
            return VersionSpec;
         }
      }

      // If we have reached this point, we found no spec for our target, so
      // we return the other spec ('*'), if we found it, or nullptr, if not.
      return FoundOtherSpec;
   }

   /// Return the availability context for the given spec.
   AvailabilityContext contextForSpec(AvailabilitySpec *Spec) {
      if (isa<OtherPlatformAvailabilitySpec>(Spec)) {
         return AvailabilityContext::alwaysAvailable();
      }

      auto *VersionSpec = cast<PlatformVersionConstraintAvailabilitySpec>(Spec);
      return AvailabilityContext(VersionRange::allGTE(VersionSpec->getVersion()));
   }

   Expr *walkToExprPost(Expr *E) override {
      if (ContextStack.back().ScopeNode.getAsExpr() == E) {
         ContextStack.pop_back();
      }

      return E;
   }
};

} // end anonymous namespace

void TypeChecker::buildTypeRefinementContextHierarchy(SourceFile &SF,
                                                      unsigned StartElem) {
   TypeRefinementContext *RootTRC = SF.getTypeRefinementContext();

   // If we are not starting at the beginning of the source file, we had better
   // already have a root type refinement context.
   assert(StartElem == 0 || RootTRC);

   AstContext &Context = SF.getAstContext();

   if (!RootTRC) {
      // The root type refinement context reflects the fact that all parts of
      // the source file are guaranteed to be executing on at least the minimum
      // platform version.
      auto MinPlatformReq = AvailabilityContext::forDeploymentTarget(Context);
      RootTRC = TypeRefinementContext::createRoot(&SF, MinPlatformReq);
      SF.setTypeRefinementContext(RootTRC);
   }

   // Build refinement contexts, if necessary, for all declarations starting
   // with StartElem.
   TypeRefinementContextBuilder Builder(RootTRC, Context);
   for (auto D : llvm::makeArrayRef(SF.Decls).slice(StartElem)) {
      Builder.build(D);
   }
}

TypeRefinementContext *
TypeChecker::getOrBuildTypeRefinementContext(SourceFile *SF) {
   TypeRefinementContext *TRC = SF->getTypeRefinementContext();
   if (!TRC) {
      buildTypeRefinementContextHierarchy(*SF, 0);
      TRC = SF->getTypeRefinementContext();
   }

   return TRC;
}

AvailabilityContext
TypeChecker::overApproximateAvailabilityAtLocation(SourceLoc loc,
                                                   const DeclContext *DC,
                                                   const TypeRefinementContext **MostRefined) {
   SourceFile *SF = DC->getParentSourceFile();
   auto &Context = DC->getAstContext();

   // If our source location is invalid (this may be synthesized code), climb
   // the decl context hierarchy until we find a location that is valid,
   // collecting availability ranges on the way up.
   // We will combine the version ranges from these annotations
   // with the TRC for the valid location to overapproximate the running
   // OS versions at the original source location.
   // Because we are climbing DeclContexts we will miss refinement contexts in
   // synthesized code that are introduced by AST elements that are themselves
   // not DeclContexts, such as  #available(..) and property declarations.
   // That is, a reference with an invalid location that is contained
   // inside a #available() and with no intermediate DeclContext will not be
   // refined. For now, this is fine -- but if we ever synthesize #available(),
   // this will be a real problem.

   // We can assume we are running on at least the minimum deployment target.
   auto OverApproximateContext =
      AvailabilityContext::forDeploymentTarget(Context);
   auto isInvalidLoc = [SF](SourceLoc loc) {
      return SF ? loc.isInvalid() : true;
   };
   while (DC && isInvalidLoc(loc)) {
      const Decl *D = DC->getInnermostDeclarationDeclContext();
      if (!D)
         break;

      loc = D->getLoc();

      Optional<AvailabilityContext> Info =
         AvailabilityInference::annotatedAvailableRange(D, Context);

      if (Info.hasValue()) {
         OverApproximateContext.constrainWith(Info.getValue());
      }

      DC = D->getDeclContext();
   }

   if (SF && loc.isValid()) {
      TypeRefinementContext *rootTRC = getOrBuildTypeRefinementContext(SF);
      TypeRefinementContext *TRC =
         rootTRC->findMostRefinedSubContext(loc, Context.SourceMgr);
      OverApproximateContext.constrainWith(TRC->getAvailabilityInfo());
      if (MostRefined) {
         *MostRefined = TRC;
      }
   }

   return OverApproximateContext;
}

bool TypeChecker::isDeclAvailable(const Decl *D, SourceLoc referenceLoc,
                                  const DeclContext *referenceDC,
                                  AvailabilityContext &OutAvailableInfo) {
   AstContext &Context = referenceDC->getAstContext();

   AvailabilityContext safeRangeUnderApprox{
      AvailabilityInference::availableRange(D, Context)};
   AvailabilityContext runningOSOverApprox =
      overApproximateAvailabilityAtLocation(referenceLoc, referenceDC);

   // The reference is safe if an over-approximation of the running OS
   // versions is fully contained within an under-approximation
   // of the versions on which the declaration is available. If this
   // containment cannot be guaranteed, we say the reference is
   // not available.
   if (!(runningOSOverApprox.isContainedIn(safeRangeUnderApprox))) {
      OutAvailableInfo = safeRangeUnderApprox;
      return false;
   }

   return true;
}

Optional<UnavailabilityReason>
TypeChecker::checkDeclarationAvailability(const Decl *D, SourceLoc referenceLoc,
                                          const DeclContext *referenceDC) {
   AstContext &Context = referenceDC->getAstContext();
   if (Context.LangOpts.DisableAvailabilityChecking) {
      return None;
   }

   if (!referenceDC->getParentSourceFile()) {
      // We only check availability if this reference is in a source file; we do
      // not check in other kinds of FileUnits.
      return None;
   }

   auto safeRangeUnderApprox = AvailabilityContext::neverAvailable();
   if (isDeclAvailable(D, referenceLoc, referenceDC, safeRangeUnderApprox)) {
      return None;
   }

   // safeRangeUnderApprox now holds the safe range.
   VersionRange version = safeRangeUnderApprox.getOSVersion();
   return UnavailabilityReason::requiresVersionRange(version);
}

void TypeChecker::diagnosePotentialUnavailability(
   const ValueDecl *D, SourceRange ReferenceRange,
   const DeclContext *ReferenceDC,
   const UnavailabilityReason &Reason) {
   diagnosePotentialUnavailability(D, D->getFullName(), ReferenceRange,
                                   ReferenceDC, Reason);
}

/// A class that walks the AST to find the innermost (i.e., deepest) node that
/// contains a target SourceRange and matches a particular criterion.
/// This class finds the innermost nodes of interest by walking
/// down the root until it has found the target range (in a Pre-visitor)
/// and then recording the innermost node on the way back up in the
/// Post-visitors. It does its best to not search unnecessary subtrees,
/// although this is complicated by the fact that not all nodes have
/// source range information.
class InnermostAncestorFinder : private AstWalker {
public:

   /// The type of a match predicate, which takes as input a node and its
   /// parent and returns a bool indicating whether the node matches.
   using MatchPredicate = std::function<bool(AstNode, AstWalker::ParentTy)>;

private:
   const SourceRange TargetRange;
   const SourceManager &SM;
   const MatchPredicate Predicate;

   bool FoundTarget = false;
   Optional<AstNode> InnermostMatchingNode;

public:
   InnermostAncestorFinder(SourceRange TargetRange, const SourceManager &SM,
                           AstNode SearchNode, const MatchPredicate &Predicate)
      : TargetRange(TargetRange), SM(SM), Predicate(Predicate) {
      assert(TargetRange.isValid());

      SearchNode.walk(*this);
   }

   /// Returns the innermost node containing the target range that matches
   /// the predicate.
   Optional<AstNode> getInnermostMatchingNode() { return InnermostMatchingNode; }

   std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
      return std::make_pair(walkToRangePre(E->getSourceRange()), E);
   }

   std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
      return std::make_pair(walkToRangePre(S->getSourceRange()), S);
   }

   bool walkToDeclPre(Decl *D) override {
      return walkToRangePre(D->getSourceRange());
   }

   std::pair<bool, Pattern *> walkToPatternPre(Pattern *P) override {
      return std::make_pair(walkToRangePre(P->getSourceRange()), P);
   }

   bool walkToTypeReprPre(TypeRepr *T) override {
      return walkToRangePre(T->getSourceRange());
   }

   /// Returns true if the walker should traverse an AST node with
   /// source range Range.
   bool walkToRangePre(SourceRange Range) {
      // When walking down the tree, we traverse until we have found a node
      // inside the target range. Once we have found such a node, there is no
      // need to traverse any deeper.
      if (FoundTarget)
         return false;

      // If we haven't found our target yet and the node we are pre-visiting
      // doesn't have a valid range, we still have to traverse it because its
      // subtrees may have valid ranges.
      if (Range.isInvalid())
         return true;

      // We have found our target if the range of the node we are visiting
      // is contained in the range we are looking for.
      FoundTarget = SM.rangeContains(TargetRange, Range);

      if (FoundTarget)
         return false;

      // Search the subtree if the target range is inside its range.
      return SM.rangeContains(Range, TargetRange);
   }

   Expr *walkToExprPost(Expr *E) override {
      if (walkToNodePost(E)) {
         return E;
      }

      return nullptr;
   }

   Stmt *walkToStmtPost(Stmt *S) override {
      if (walkToNodePost(S)) {
         return S;
      }

      return nullptr;
   }

   bool walkToDeclPost(Decl *D) override {
      return walkToNodePost(D);
   }

   /// Once we have found the target node, look for the innermost ancestor
   /// matching our criteria on the way back up the spine of the tree.
   bool walkToNodePost(AstNode Node) {
      if (!InnermostMatchingNode.hasValue() && Predicate(Node, Parent)) {
         assert(Node.getSourceRange().isInvalid() ||
                SM.rangeContains(Node.getSourceRange(), TargetRange));

         InnermostMatchingNode = Node;
         return false;
      }

      return true;
   }
};

/// Starting from SearchRoot, finds the innermost node containing ChildRange
/// for which Predicate returns true. Returns None if no such root is found.
static Optional<AstNode> findInnermostAncestor(
   SourceRange ChildRange, const SourceManager &SM, AstNode SearchRoot,
   const InnermostAncestorFinder::MatchPredicate &Predicate) {
   InnermostAncestorFinder Finder(ChildRange, SM, SearchRoot, Predicate);
   return Finder.getInnermostMatchingNode();
}

/// Given a reference range and a declaration context containing the range,
/// attempt to find a declaration containing the reference. This may not
/// be the innermost declaration containing the range.
/// Returns null if no such declaration can be found.
static const Decl *findContainingDeclaration(SourceRange ReferenceRange,
                                             const DeclContext *ReferenceDC,
                                             const SourceManager &SM) {
   auto ContainsReferenceRange = [&](const Decl *D) -> bool {
      if (ReferenceRange.isInvalid())
         return false;

      // Members of an active #if are represented both inside the
      // IfConfigDecl and in the enclosing context. Skip over the IfConfigDecl
      // so that that the member declaration is found rather the #if itself.
      if (isa<IfConfigDecl>(D))
         return false;

      return SM.rangeContains(D->getSourceRange(), ReferenceRange);
   };

   if (const Decl *D = ReferenceDC->getInnermostDeclarationDeclContext()) {
      // If we have an inner declaration context, see if we can narrow the search
      // down to one of its members. This is important for properties, which don't
      // count as DeclContexts of their own but which can still introduce
      // availability.
      if (auto *IDC = dyn_cast<IterableDeclContext>(D)) {
         auto BestMember = llvm::find_if(IDC->getMembers(),
                                         ContainsReferenceRange);
         if (BestMember != IDC->getMembers().end())
            return *BestMember;
      }
      return D;
   }

   // We couldn't find a suitable node by climbing the DeclContext hierarchy, so
   // fall back to looking for a top-level declaration that contains the
   // reference range. We will hit this case for top-level elements that do not
   // themselves introduce DeclContexts, such as global variables. If we don't
   // have a reference range, there is nothing we can do, so return null.
   if (ReferenceRange.isInvalid())
      return nullptr;

   SourceFile *SF = ReferenceDC->getParentSourceFile();
   if (!SF)
      return nullptr;

   auto BestTopLevelDecl = llvm::find_if(SF->Decls, ContainsReferenceRange);
   if (BestTopLevelDecl != SF->Decls.end())
      return *BestTopLevelDecl;

   return nullptr;
}

/// Given a declaration that allows availability attributes in the abstract
/// syntax tree, return the declaration upon which the declaration would
/// appear in concrete syntax. This function is necessary because for semantic
/// analysis, the parser attaches attributes to declarations other
/// than those on which they, concretely, appear. For these declarations (enum
/// cases and variable declarations) a Fix-It for an added availability
/// attribute should be suggested for the appropriate concrete location.
static const Decl *
concreteSyntaxDeclForAvailableAttribute(const Decl *AbstractSyntaxDecl) {
   // This function needs to be kept in sync with its counterpart,
   // abstractSyntaxDeclForAvailableAttribute().

   // The source range for VarDecls does not include 'var ' (and, in any
   // event, multiple variables can be introduced with a single 'var'),
   // so suggest adding an attribute to the PatterningBindingDecl instead.
   if (auto *VD = dyn_cast<VarDecl>(AbstractSyntaxDecl)) {
      return VD->getParentPatternBinding();
   }

   // Similarly suggest applying the Fix-It to the parent enum case rather than
   // the enum element.
   if (auto *EE = dyn_cast<EnumElementDecl>(AbstractSyntaxDecl)) {
      return EE->getParentCase();
   }

   return AbstractSyntaxDecl;
}

/// Given a declaration upon which an availability attribute would appear in
/// concrete syntax, return a declaration to which the parser
/// actually attaches the attribute in the abstract syntax tree. We use this
/// function to determine whether the concrete syntax already has an
/// availability attribute.
static const Decl *
abstractSyntaxDeclForAvailableAttribute(const Decl *ConcreteSyntaxDecl) {
   // This function needs to be kept in sync with its counterpart,
   // concreteSyntaxDeclForAvailableAttribute().

   if (auto *PBD = dyn_cast<PatternBindingDecl>(ConcreteSyntaxDecl)) {
      // Existing @available attributes in the AST are attached to VarDecls
      // rather than PatternBindingDecls, so we return the first VarDecl for
      // the pattern binding declaration.
      // This is safe, even though there may be multiple VarDecls, because
      // all parsed attribute that appear in the concrete syntax upon on the
      // PatternBindingDecl are added to all of the VarDecls for the pattern
      // binding.
      if (PBD->getNumPatternEntries() != 0) {
         return PBD->getAnchoringVarDecl(0);
      }
   } else if (auto *ECD = dyn_cast<EnumCaseDecl>(ConcreteSyntaxDecl)) {
      // Similar to the PatternBindingDecl case above, we return the
      // first EnumElementDecl.
      ArrayRef<EnumElementDecl *> Elems = ECD->getElements();
      if (!Elems.empty()) {
         return Elems.front();
      }
   }

   return ConcreteSyntaxDecl;
}

/// Given a declaration, return a better related declaration for which
/// to suggest an @available fixit, or the original declaration
/// if no such related declaration exists.
static const Decl *relatedDeclForAvailabilityFixit(const Decl *D) {
   if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
      // Suggest @available Fix-Its on property rather than individual
      // accessors.
      D = accessor->getStorage();
   }

   return abstractSyntaxDeclForAvailableAttribute(D);
}

/// Walk the DeclContext hierarchy starting from D to find a declaration
/// at the member level (i.e., declared in a type context) on which to provide
/// an @available() Fix-It.
static const Decl *ancestorMemberLevelDeclForAvailabilityFixit(const Decl *D) {
   while (D) {
      D = relatedDeclForAvailabilityFixit(D);

      if (D->getDeclContext()->isTypeContext() &&
          DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::DAK_Available,
                                                  D)) {
         break;
      }

      D = cast_or_null<AbstractFunctionDecl>(
         D->getDeclContext()->getInnermostMethodContext());
   }

   return D;
}

/// Returns true if the declaration is at the type level (either a nominal
/// type, an extension, or a global function) and can support an @available
/// attribute.
static bool isTypeLevelDeclForAvailabilityFixit(const Decl *D) {
   if (!DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind::DAK_Available,
                                                D)) {
      return false;
   }

   if (isa<ExtensionDecl>(D) || isa<NominalTypeDecl>(D)) {
      return true;
   }

   bool IsModuleScopeContext = D->getDeclContext()->isModuleScopeContext();

   // We consider global functions to be "type level"
   if (isa<FuncDecl>(D)) {
      return IsModuleScopeContext;
   }

   if (auto *VD = dyn_cast<VarDecl>(D)) {
      if (!IsModuleScopeContext)
         return false;

      if (PatternBindingDecl *PBD = VD->getParentPatternBinding()) {
         return PBD->getDeclContext()->isModuleScopeContext();
      }
   }

   return false;
}

/// Walk the DeclContext hierarchy starting from D to find a declaration
/// at a member level (i.e., declared in a type context) on which to provide an
/// @available() Fix-It.
static const Decl *ancestorTypeLevelDeclForAvailabilityFixit(const Decl *D) {
   assert(D);

   D = relatedDeclForAvailabilityFixit(D);

   while (D && !isTypeLevelDeclForAvailabilityFixit(D)) {
      D = D->getDeclContext()->getInnermostDeclarationDeclContext();
   }

   return D;
}

/// Given the range of a reference to an unavailable symbol and the
/// declaration context containing the reference, make a best effort find up to
/// three locations for potential fixits.
///
/// \param FoundVersionCheckNode Returns a node that can be wrapped in a
/// if #available(...) { ... } version check to fix the unavailable reference,
/// or None if such a node cannot be found.
///
/// \param FoundMemberLevelDecl Returns member-level declaration (i.e., the
///  child of a type DeclContext) for which an @available attribute would
/// fix the unavailable reference.
///
/// \param FoundTypeLevelDecl returns a type-level declaration (a
/// a nominal type, an extension, or a global function) for which an
/// @available attribute would fix the unavailable reference.
static void findAvailabilityFixItNodes(SourceRange ReferenceRange,
                                       const DeclContext *ReferenceDC,
                                       const SourceManager &SM,
                                       Optional<AstNode> &FoundVersionCheckNode,
                                       const Decl *&FoundMemberLevelDecl,
                                       const Decl *&FoundTypeLevelDecl) {
   FoundVersionCheckNode = None;
   FoundMemberLevelDecl = nullptr;
   FoundTypeLevelDecl = nullptr;

   // Limit tree to search based on the DeclContext of the reference.
   const Decl *DeclarationToSearch =
      findContainingDeclaration(ReferenceRange, ReferenceDC, SM);
   if (!DeclarationToSearch)
      return;

   // Const-cast to inject into AstNode. This search will not modify
   // the declaration.
   AstNode SearchRoot = const_cast<Decl *>(DeclarationToSearch);

   // The node to wrap in if #available(...) { ... } is the innermost node in
   // SearchRoot that (1) can be guarded with an if statement and (2)
   // contains the ReferenceRange.
   // We make no guarantee that the Fix-It, when applied, will result in
   // semantically valid code -- but, at a minimum, it should parse. So,
   // for example, we may suggest wrapping a variable declaration in a guard,
   // which would not be valid if the variable is later used. The goal
   // is discoverability of #os() (via the diagnostic and Fix-It) rather than
   // magically fixing the code in all cases.

   InnermostAncestorFinder::MatchPredicate IsGuardable =
      [](AstNode Node, AstWalker::ParentTy Parent) {
         if (Expr *ParentExpr = Parent.getAsExpr()) {
            auto *ParentClosure = dyn_cast<ClosureExpr>(ParentExpr);
            if (!ParentClosure || !ParentClosure->hasSingleExpressionBody()) {
               return false;
            }
         } else if (auto *ParentStmt = Parent.getAsStmt()) {
            if (!isa<BraceStmt>(ParentStmt)) {
               return false;
            }
         } else {
            return false;
         }

         return true;
      };

   FoundVersionCheckNode =
      findInnermostAncestor(ReferenceRange, SM, SearchRoot, IsGuardable);

   // Try to find declarations on which @available attributes can be added.
   // The heuristics for finding these declarations are biased towards deeper
   // nodes in the AST to limit the scope of suggested availability regions
   // and provide a better IDE experience (it can get jumpy if Fix-It locations
   // are far away from the error needing the Fix-It).
   if (DeclarationToSearch) {
      FoundMemberLevelDecl =
         ancestorMemberLevelDeclForAvailabilityFixit(DeclarationToSearch);

      FoundTypeLevelDecl =
         ancestorTypeLevelDeclForAvailabilityFixit(DeclarationToSearch);
   }
}

/// Emit a diagnostic note and Fix-It to add an @available attribute
/// on the given declaration for the given version range.
static void fixAvailabilityForDecl(SourceRange ReferenceRange, const Decl *D,
                                   const VersionRange &RequiredRange,
                                   AstContext &Context) {
   assert(D);

   // Don't suggest adding an @available() to a declaration where we would
   // emit a diagnostic saying it is not allowed.
   if (TypeChecker::diagnosticIfDeclCannotBePotentiallyUnavailable(D).hasValue())
      return;

   if (getActiveAvailableAttribute(D, Context)) {
      // For QoI, in future should emit a fixit to update the existing attribute.
      return;
   }

   // For some declarations (variables, enum elements), the location in concrete
   // syntax to suggest the Fix-It may differ from the declaration to which
   // we attach availability attributes in the abstract syntax tree during
   // parsing.
   const Decl *ConcDecl = concreteSyntaxDeclForAvailableAttribute(D);

   DescriptiveDeclKind KindForDiagnostic = ConcDecl->getDescriptiveKind();
   SourceLoc InsertLoc;

   // To avoid exposing the pattern binding declaration to the user, get the
   // descriptive kind from one of the VarDecls. We get the Fix-It location
   // from the PatternBindingDecl unless the VarDecl has attributes,
   // in which case we get the start location of the VarDecl attributes.
   DeclAttributes AttrsForLoc;
   if (KindForDiagnostic == DescriptiveDeclKind::PatternBinding) {
      KindForDiagnostic = D->getDescriptiveKind();
      AttrsForLoc = D->getAttrs();
   } else {
      InsertLoc = ConcDecl->getAttrs().getStartLoc(/*forModifiers=*/false);
   }

   InsertLoc = D->getAttrs().getStartLoc(/*forModifiers=*/false);
   if (InsertLoc.isInvalid()) {
      InsertLoc = ConcDecl->getStartLoc();
   }

   if (InsertLoc.isInvalid())
      return;

   StringRef OriginalIndent =
      Lexer::getIndentationForLine(Context.SourceMgr, InsertLoc);
   PlatformKind Target = targetPlatform(Context.LangOpts);

   D->diagnose(diag::availability_add_attribute, KindForDiagnostic)
      .fixItInsert(InsertLoc, diag::insert_available_attr,
                   platformString(Target),
                   RequiredRange.getLowerEndpoint().getAsString(),
                   OriginalIndent);
}

/// In the special case of being in an existing, nontrivial type refinement
/// context that's close but not quite narrow enough to satisfy requirements
/// (i.e.  requirements are contained-in the existing TRC but off by a subminor
/// version), emit a diagnostic and fixit that narrows the existing TRC
/// condition to the required range.
static bool fixAvailabilityByNarrowingNearbyVersionCheck(
   SourceRange ReferenceRange,
   const DeclContext *ReferenceDC,
   const VersionRange &RequiredRange,
   AstContext &Context,
   InFlightDiagnostic &Err) {
   const TypeRefinementContext *TRC = nullptr;
   AvailabilityContext RunningOSOverApprox =
      TypeChecker::overApproximateAvailabilityAtLocation(ReferenceRange.start,
                                                         ReferenceDC, &TRC);
   VersionRange RunningRange = RunningOSOverApprox.getOSVersion();
   if (RunningRange.hasLowerEndpoint() &&
       RequiredRange.hasLowerEndpoint() &&
       AvailabilityContext(RequiredRange).isContainedIn(RunningOSOverApprox) &&
       TRC && TRC->getReason() != TypeRefinementContext::Reason::Root) {

      // Only fix situations that are "nearby" versions, meaning
      // disagreement on a minor-or-less version for non-macOS,
      // or disagreement on a subminor-or-less version for macOS.
      auto RunningVers = RunningRange.getLowerEndpoint();
      auto RequiredVers = RequiredRange.getLowerEndpoint();
      auto Platform = targetPlatform(Context.LangOpts);
      if (RunningVers.getMajor() != RequiredVers.getMajor())
         return false;
      if ((Platform == PlatformKind::OSX ||
           Platform == PlatformKind::OSXApplicationExtension) &&
          !(RunningVers.getMinor().hasValue() &&
            RequiredVers.getMinor().hasValue() &&
            RunningVers.getMinor().getValue() ==
            RequiredVers.getMinor().getValue()))
         return false;

      auto FixRange = TRC->getAvailabilityConditionVersionSourceRange(
         Platform, RunningVers);
      if (!FixRange.isValid())
         return false;
      // Have found a nontrivial type refinement context-introducer to narrow.
      Err.fixItReplace(FixRange, RequiredVers.getAsString());
      return true;
   }
   return false;
}

/// Emit a diagnostic note and Fix-It to add an if #available(...) { } guard
/// that checks for the given version range around the given node.
static void fixAvailabilityByAddingVersionCheck(
   AstNode NodeToWrap, const VersionRange &RequiredRange,
   SourceRange ReferenceRange, AstContext &Context) {
   SourceRange RangeToWrap = NodeToWrap.getSourceRange();
   if (RangeToWrap.isInvalid())
      return;

   SourceLoc ReplaceLocStart = RangeToWrap.start;
   StringRef ExtraIndent;
   StringRef OriginalIndent = Lexer::getIndentationForLine(
      Context.SourceMgr, ReplaceLocStart, &ExtraIndent);

   std::string IfText;
   {
      llvm::raw_string_ostream Out(IfText);

      SourceLoc ReplaceLocEnd =
         Lexer::getLocForEndOfToken(Context.SourceMgr, RangeToWrap.end);

      std::string GuardedText =
         Context.SourceMgr.extractText(CharSourceRange(Context.SourceMgr,
                                                       ReplaceLocStart,
                                                       ReplaceLocEnd)).str();

      std::string NewLine = "\n";
      std::string NewLineReplacement = (NewLine + ExtraIndent).str();

      // Indent the body of the Fix-It if. Because the body may be a compound
      // statement, we may have to indent multiple lines.
      size_t StartAt = 0;
      while ((StartAt = GuardedText.find(NewLine, StartAt)) !=
             std::string::npos) {
         GuardedText.replace(StartAt, NewLine.length(), NewLineReplacement);
         StartAt += NewLine.length();
      }

      PlatformKind Target = targetPlatform(Context.LangOpts);

      Out << "if #available(" << platformString(Target)
          << " " << RequiredRange.getLowerEndpoint().getAsString()
          << ", *) {\n";

      Out << OriginalIndent << ExtraIndent << GuardedText << "\n";

      // We emit an empty fallback case with a comment to encourage the developer
      // to think explicitly about whether fallback on earlier versions is needed.
      Out << OriginalIndent << "} else {\n";
      Out << OriginalIndent << ExtraIndent << "// Fallback on earlier versions\n";
      Out << OriginalIndent << "}";
   }

   Context.Diags.diagnose(
         ReferenceRange.start, diag::availability_guard_with_version_check)
      .fixItReplace(RangeToWrap, IfText);
}

/// Emit suggested Fix-Its for a reference with to an unavailable symbol
/// requiting the given OS version range.
static void fixAvailability(SourceRange ReferenceRange,
                            const DeclContext *ReferenceDC,
                            const VersionRange &RequiredRange,
                            AstContext &Context) {
   if (ReferenceRange.isInvalid())
      return;

   Optional<AstNode> NodeToWrapInVersionCheck;
   const Decl *FoundMemberDecl = nullptr;
   const Decl *FoundTypeLevelDecl = nullptr;

   findAvailabilityFixItNodes(ReferenceRange, ReferenceDC, Context.SourceMgr,
                              NodeToWrapInVersionCheck, FoundMemberDecl,
                              FoundTypeLevelDecl);

   // Suggest wrapping in if #available(...) { ... } if possible.
   if (NodeToWrapInVersionCheck.hasValue()) {
      fixAvailabilityByAddingVersionCheck(NodeToWrapInVersionCheck.getValue(),
                                          RequiredRange, ReferenceRange, Context);
   }

   // Suggest adding availability attributes.
   if (FoundMemberDecl) {
      fixAvailabilityForDecl(ReferenceRange, FoundMemberDecl, RequiredRange,
                             Context);
   }

   if (FoundTypeLevelDecl) {
      fixAvailabilityForDecl(ReferenceRange, FoundTypeLevelDecl, RequiredRange,
                             Context);
   }
}

void TypeChecker::diagnosePotentialOpaqueTypeUnavailability(
   SourceRange ReferenceRange, const DeclContext *ReferenceDC,
   const UnavailabilityReason &Reason) {
   AstContext &Context = ReferenceDC->getAstContext();

   // We only emit diagnostics for API unavailability, not for explicitly
   // weak-linked symbols.
   if (Reason.getReasonKind() !=
       UnavailabilityReason::Kind::RequiresOSVersionRange) {
      return;
   }

   auto RequiredRange = Reason.getRequiredOSVersionRange();
   {
      auto Err =
         Context.Diags.diagnose(
            ReferenceRange.start, diag::availability_opaque_types_only_version_newer,
            prettyPlatformString(targetPlatform(Context.LangOpts)),
            Reason.getRequiredOSVersionRange().getLowerEndpoint());

      // Direct a fixit to the error if an existing guard is nearly-correct
      if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
                                                       ReferenceDC,
                                                       RequiredRange, Context, Err))
         return;
   }
   fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

void TypeChecker::diagnosePotentialUnavailability(
   const Decl *D, DeclName Name, SourceRange ReferenceRange,
   const DeclContext *ReferenceDC, const UnavailabilityReason &Reason) {
   AstContext &Context = ReferenceDC->getAstContext();

   // We only emit diagnostics for API unavailability, not for explicitly
   // weak-linked symbols.
   if (Reason.getReasonKind() !=
       UnavailabilityReason::Kind::RequiresOSVersionRange) {
      return;
   }

   auto RequiredRange = Reason.getRequiredOSVersionRange();
   {
      auto Err =
         Context.Diags.diagnose(
            ReferenceRange.start, diag::availability_decl_only_version_newer,
            Name, prettyPlatformString(targetPlatform(Context.LangOpts)),
            Reason.getRequiredOSVersionRange().getLowerEndpoint());

      // Direct a fixit to the error if an existing guard is nearly-correct
      if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
                                                       ReferenceDC,
                                                       RequiredRange, Context, Err))
         return;
   }

   fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

void TypeChecker::diagnosePotentialAccessorUnavailability(
   const AccessorDecl *Accessor, SourceRange ReferenceRange,
   const DeclContext *ReferenceDC, const UnavailabilityReason &Reason,
   bool ForInout) {
   AstContext &Context = ReferenceDC->getAstContext();

   assert(Accessor->isGetterOrSetter());

   const AbstractStorageDecl *ASD = Accessor->getStorage();
   DeclName Name = ASD->getFullName();

   auto &diag = ForInout ? diag::availability_inout_accessor_only_version_newer
                         : diag::availability_accessor_only_version_newer;

   auto RequiredRange = Reason.getRequiredOSVersionRange();
   {
      auto Err =
         Context.Diags.diagnose(
            ReferenceRange.start, diag,
            static_cast<unsigned>(Accessor->getAccessorKind()), Name,
            prettyPlatformString(targetPlatform(Context.LangOpts)),
            Reason.getRequiredOSVersionRange().getLowerEndpoint());


      // Direct a fixit to the error if an existing guard is nearly-correct
      if (fixAvailabilityByNarrowingNearbyVersionCheck(ReferenceRange,
                                                       ReferenceDC,
                                                       RequiredRange, Context, Err))
         return;
   }

   fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

const AvailableAttr *TypeChecker::getDeprecated(const Decl *D) {
   if (auto *Attr = D->getAttrs().getDeprecated(D->getAstContext()))
      return Attr;

   // Treat extensions methods as deprecated if their extension
   // is deprecated.
   DeclContext *DC = D->getDeclContext();
   if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
      return getDeprecated(ED);
   }

   return nullptr;
}

/// Returns true if some declaration lexically enclosing the reference
/// matches the passed in predicate and false otherwise.
static bool
someEnclosingDeclMatches(SourceRange ReferenceRange,
                         const DeclContext *ReferenceDC,
                         llvm::function_ref<bool(const Decl *)> Pred) {
   // Climb the DeclContext hierarchy to see if any of the containing
   // declarations matches the predicate.
   const DeclContext *DC = ReferenceDC;
   while (true) {
      auto *D = DC->getInnermostDeclarationDeclContext();
      if (!D)
         break;

      if (Pred(D)) {
         return true;
      }

      // If we are in an accessor, check to see if the associated
      // property matches the predicate.
      if (auto accessor = dyn_cast<AccessorDecl>(D)) {
         if (Pred(accessor->getStorage()))
            return true;
      }

      DC = D->getDeclContext();
   }

   // Search the AST starting from our innermost declaration context to see if
   // if the reference is inside a property declaration but not inside an
   // accessor (this can happen for the TypeRepr for the declared type of a
   // property, for example).
   // We can't rely on the DeclContext hierarchy climb above because properties
   // do not introduce a new DeclContext.

   // Don't search for a containing declaration if we don't have a source range.
   if (ReferenceRange.isInvalid())
      return false;

   AstContext &Ctx = ReferenceDC->getAstContext();
   const Decl *DeclToSearch =
      findContainingDeclaration(ReferenceRange, ReferenceDC, Ctx.SourceMgr);

   // We may not be able to find a declaration to search if the ReferenceRange
   // isn't useful (i.e., we are in synthesized code).
   if (!DeclToSearch)
      return false;

   return Pred(abstractSyntaxDeclForAvailableAttribute(DeclToSearch));
}

/// Returns true if the reference or any of its parents is an
/// implicit function.
static bool isInsideImplicitFunction(SourceRange ReferenceRange,
                                     const DeclContext *DC) {
   auto IsInsideImplicitFunc = [](const Decl *D) {
      auto *AFD = dyn_cast<AbstractFunctionDecl>(D);
      return AFD && AFD->isImplicit();
   };

   return someEnclosingDeclMatches(ReferenceRange, DC, IsInsideImplicitFunc);
}

/// Returns true if the reference or any of its parents is an
/// unavailable (or obsoleted) declaration.
static bool isInsideUnavailableDeclaration(SourceRange ReferenceRange,
                                           const DeclContext *ReferenceDC) {
   auto IsUnavailable = [](const Decl *D) {
      return D->getAttrs().getUnavailable(D->getAstContext());
   };

   return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable);
}

/// Returns true if the reference or any of its parents is an
/// unconditional unavailable declaration for the same platform.
static bool isInsideCompatibleUnavailableDeclaration(
   const ValueDecl *referencedD, SourceRange ReferenceRange,
   const DeclContext *ReferenceDC, const AvailableAttr *attr) {
   if (!attr->isUnconditionallyUnavailable()) {
      return false;
   }

   // Refuse calling unavailable functions from unavailable code,
   // but allow the use of types.
   PlatformKind platform = attr->Platform;
   if (platform == PlatformKind::none &&
       !isa<TypeDecl>(referencedD)) {
      return false;
   }

   auto IsUnavailable = [platform](const Decl *D) {
      auto EnclosingUnavailable =
         D->getAttrs().getUnavailable(D->getAstContext());
      return EnclosingUnavailable && EnclosingUnavailable->Platform == platform;
   };

   return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsUnavailable);
}

/// Returns true if the reference is lexically contained in a declaration
/// that is deprecated on all deployment targets.
static bool isInsideDeprecatedDeclaration(SourceRange ReferenceRange,
                                          const DeclContext *ReferenceDC){
   auto IsDeprecated = [](const Decl *D) {
      return D->getAttrs().getDeprecated(D->getAstContext());
   };

   return someEnclosingDeclMatches(ReferenceRange, ReferenceDC, IsDeprecated);
}

static void fixItAvailableAttrRename(InFlightDiagnostic &diag,
                                     SourceRange referenceRange,
                                     const ValueDecl *renamedDecl,
                                     const AvailableAttr *attr,
                                     const ApplyExpr *call) {
   if (isa<AccessorDecl>(renamedDecl))
      return;

   ParsedDeclName parsed = parse_decl_name(attr->Rename);
   if (!parsed)
      return;

   bool originallyWasKnownOperatorExpr = false;
   if (call) {
      originallyWasKnownOperatorExpr =
         isa<BinaryExpr>(call) ||
         isa<PrefixUnaryExpr>(call) ||
         isa<PostfixUnaryExpr>(call);
   }
   if (parsed.isOperator() != originallyWasKnownOperatorExpr)
      return;

   auto &ctx = renamedDecl->getAstContext();
   SourceManager &sourceMgr = ctx.SourceMgr;

   if (parsed.isInstanceMember()) {
      // Replace the base of the call with the "self argument".
      // We can only do a good job with the fix-it if we have the whole call
      // expression.
      // FIXME: Should we be validating the ContextName in some way?
      if (!call || !isa<CallExpr>(call))
         return;

      unsigned selfIndex = parsed.SelfIndex.getValue();
      const Expr *selfExpr = nullptr;
      SourceLoc removeRangeStart;
      SourceLoc removeRangeEnd;

      auto *argExpr = call->getArg();
      auto argList = getOriginalArgumentList(argExpr);

      size_t numElementsWithinParens = argList.args.size();
      numElementsWithinParens -= argList.hasTrailingClosure;
      if (selfIndex >= numElementsWithinParens)
         return;

      if (parsed.IsGetter) {
         if (numElementsWithinParens != 1)
            return;
      } else if (parsed.IsSetter) {
         if (numElementsWithinParens != 2)
            return;
      } else {
         if (parsed.ArgumentLabels.size() != argList.args.size() - 1)
            return;
      }

      selfExpr = argList.args[selfIndex];

      if (selfIndex + 1 == numElementsWithinParens) {
         if (selfIndex > 0) {
            // Remove from the previous comma to the close-paren (half-open).
            removeRangeStart = argList.args[selfIndex-1]->getEndLoc();
            removeRangeStart = Lexer::getLocForEndOfToken(sourceMgr,
                                                          removeRangeStart);
         } else {
            // Remove from after the open paren to the close paren (half-open).
            removeRangeStart = Lexer::getLocForEndOfToken(sourceMgr,
                                                          argExpr->getStartLoc());
         }

         // Prefer the r-paren location, so that we get the right behavior when
         // there's a trailing closure, but handle some implicit cases too.
         removeRangeEnd = argList.rParenLoc;
         if (removeRangeEnd.isInvalid())
            removeRangeEnd = argExpr->getEndLoc();

      } else {
         // Remove from the label to the start of the next argument (half-open).
         SourceLoc labelLoc = argList.labelLocs[selfIndex];
         if (labelLoc.isValid())
            removeRangeStart = labelLoc;
         else
            removeRangeStart = selfExpr->getStartLoc();

         SourceLoc nextLabelLoc = argList.labelLocs[selfIndex + 1];
         if (nextLabelLoc.isValid())
            removeRangeEnd = nextLabelLoc;
         else
            removeRangeEnd = argList.args[selfIndex + 1]->getStartLoc();
      }

      // Avoid later argument label fix-its for this argument.
      if (!parsed.isPropertyAccessor()) {
         Identifier oldLabel = argList.labels[selfIndex];
         StringRef oldLabelStr;
         if (!oldLabel.empty())
            oldLabelStr = oldLabel.str();
         parsed.ArgumentLabels.insert(parsed.ArgumentLabels.begin() + selfIndex,
                                      oldLabelStr);
      }

      if (auto *inoutSelf = dyn_cast<InOutExpr>(selfExpr))
         selfExpr = inoutSelf->getSubExpr();

      CharSourceRange selfExprRange =
         Lexer::getCharSourceRangeFromSourceRange(sourceMgr,
                                                  selfExpr->getSourceRange());
      bool needsParens = !selfExpr->canAppendPostfixExpression();

      SmallString<64> selfReplace;
      if (needsParens)
         selfReplace.push_back('(');

      // If the base is contextual member lookup and we know the type,
      // let's just prepend it, otherwise we'll end up with an incorrect fix-it.
      auto base = sourceMgr.extractText(selfExprRange);
      if (!base.empty() && base.front() == '.') {
         auto newName = attr->Rename;
         // If this is not a rename, let's not
         // even try to emit a fix-it because
         // it's going to be invalid.
         if (newName.empty())
            return;

         auto parts = newName.split('.');
         auto nominalName = parts.first;
         assert(!nominalName.empty());

         selfReplace += nominalName;
      }

      selfReplace += base;
      if (needsParens)
         selfReplace.push_back(')');
      selfReplace.push_back('.');
      selfReplace += parsed.BaseName;
      diag.fixItReplace(call->getFn()->getSourceRange(), selfReplace);

      if (!parsed.isPropertyAccessor())
         diag.fixItRemoveChars(removeRangeStart, removeRangeEnd);

      // Continue on to diagnose any argument label renames.

   } else if (parsed.BaseName == "init" &&
              call && isa<CallExpr>(call)) {
      // For initializers, replace with a "call" of the context type...but only
      // if we know we're doing a call (rather than a first-class reference).
      if (parsed.isMember()) {
         diag.fixItReplace(call->getFn()->getSourceRange(), parsed.ContextName);

      } else if (auto *dotCall = dyn_cast<DotSyntaxCallExpr>(call->getFn())) {
         SourceLoc removeLoc = dotCall->getDotLoc();
         if (removeLoc.isInvalid())
            return;

         diag.fixItRemove(SourceRange(removeLoc, dotCall->getFn()->getEndLoc()));
      } else if (!isa<ConstructorRefCallExpr>(call->getFn())) {
         return;
      }

      // Continue on to diagnose any constructor argument label renames.

   } else {
      // Just replace the base name.
      SmallString<64> baseReplace;
      if (!parsed.ContextName.empty()) {
         baseReplace += parsed.ContextName;
         baseReplace += '.';
      }
      baseReplace += parsed.BaseName;
      if (parsed.IsFunctionName && parsed.ArgumentLabels.empty() &&
          isa<VarDecl>(renamedDecl)) {
         // If we're going from a var to a function with no arguments, emit an
         // empty parameter list.
         baseReplace += "()";
      }
      diag.fixItReplace(referenceRange, baseReplace);
   }

   if (!call || !isa<CallExpr>(call))
      return;

   auto *argExpr = call->getArg();
   auto argList = getOriginalArgumentList(argExpr);

   if (parsed.IsGetter) {
      diag.fixItRemove(argExpr->getSourceRange());
      return;
   }

   if (parsed.IsSetter) {
      const Expr *newValueExpr = nullptr;

      if (argList.args.size() >= 1) {
         size_t newValueIndex = 0;
         if (parsed.isInstanceMember()) {
            assert(parsed.SelfIndex.getValue() == 0 ||
                   parsed.SelfIndex.getValue() == 1);
            newValueIndex = !parsed.SelfIndex.getValue();
         }
         newValueExpr = argList.args[newValueIndex];
      } else {
         newValueExpr = argList.args[0];
      }

      diag.fixItReplaceChars(argExpr->getStartLoc(), newValueExpr->getStartLoc(),
                             " = ");
      diag.fixItRemoveChars(Lexer::getLocForEndOfToken(sourceMgr,
                                                       newValueExpr->getEndLoc()),
                            Lexer::getLocForEndOfToken(sourceMgr,
                                                       argExpr->getEndLoc()));
      return;
   }

   if (!parsed.IsFunctionName)
      return;

   SmallVector<Identifier, 4> argumentLabelIDs;
   std::transform(parsed.ArgumentLabels.begin(), parsed.ArgumentLabels.end(),
                  std::back_inserter(argumentLabelIDs),
                  [&ctx](StringRef labelStr) -> Identifier {
                     return labelStr.empty() ? Identifier() : ctx.getIdentifier(labelStr);
                  });

   // Coerce the `argumentLabelIDs` to the user supplied arguments.
   // e.g:
   //   @available(.., renamed: "new(w:x:y:z:)")
   //   func old(a: Int, b: Int..., c: String="", d: Int=0){}
   //   old(a: 1, b: 2, 3, 4, d: 5)
   // coerce
   //   argumentLabelIDs = {"w", "x", "y", "z"}
   // to
   //   argumentLabelIDs = {"w", "x", "", "", "z"}
   auto I = argumentLabelIDs.begin();

   auto updateLabelsForArg = [&](Expr *expr) -> bool {
      if (isa<DefaultArgumentExpr>(expr)) {
         // Defaulted: remove param label of it.
         if (I == argumentLabelIDs.end())
            return true;

         I = argumentLabelIDs.erase(I);
         return false;
      }

      if (auto *varargExpr = dyn_cast<VarargExpansionExpr>(expr)) {
         if (auto *arrayExpr = dyn_cast<ArrayExpr>(varargExpr->getSubExpr())) {
            auto variadicArgsNum = arrayExpr->getNumElements();
            if (variadicArgsNum == 0) {
               // No arguments: Remove param label of it.
               I = argumentLabelIDs.erase(I);
            } else if (variadicArgsNum == 1) {
               // One argument: Just advance.
               ++I;
            } else {
               ++I;

               // Two or more arguments: Insert empty labels after the first one.
               variadicArgsNum--;
               I = argumentLabelIDs.insert(I, variadicArgsNum, Identifier());
               I += variadicArgsNum;
            }
            return false;
         }
      }

      // Normal: Just advance.
      if (I == argumentLabelIDs.end())
         return true;

      ++I;
      return false;
   };

   if (auto *parenExpr = dyn_cast<ParenExpr>(argExpr)) {
      if (updateLabelsForArg(parenExpr->getSubExpr()))
         return;
   } else {
      for (auto *arg : cast<TupleExpr>(argExpr)->getElements()) {
         if (updateLabelsForArg(arg))
            return;
      }
   }

   if (argumentLabelIDs.size() != argList.args.size()) {
      // Mismatched lengths; give up.
      return;
   }

   auto argumentLabelsToCheck = llvm::makeArrayRef(argumentLabelIDs);
   // The argument label for a trailing closure is ignored.
   if (argList.hasTrailingClosure)
      argumentLabelsToCheck = argumentLabelsToCheck.drop_back();

   if (std::equal(argumentLabelsToCheck.begin(), argumentLabelsToCheck.end(),
                  argList.labels.begin())) {
      // Already matching.
      return;
   }

   diagnoseArgumentLabelError(ctx, argExpr, argumentLabelIDs, false, &diag);
}

// Must be kept in sync with diag::availability_decl_unavailable_rename and
// others.
namespace {
enum class ReplacementDeclKind : unsigned {
   None,
   InstanceMethod,
   Property,
};
} // end anonymous namespace

static Optional<ReplacementDeclKind>
describeRename(AstContext &ctx, const AvailableAttr *attr, const ValueDecl *D,
               SmallVectorImpl<char> &nameBuf) {
   ParsedDeclName parsed = parse_decl_name(attr->Rename);
   if (!parsed)
      return None;

   // Only produce special descriptions for renames to
   // - instance members
   // - properties (or global bindings)
   // - class/static methods
   // - initializers, unless the original was known to be an initializer
   // Leave non-member renames alone, as well as renames from top-level types
   // and bindings to member types and class/static properties.
   if (!(parsed.isInstanceMember() || parsed.isPropertyAccessor() ||
         (parsed.isMember() && parsed.IsFunctionName) ||
         (parsed.BaseName == "init" &&
          !dyn_cast_or_null<ConstructorDecl>(D)))) {
      return None;
   }

   llvm::raw_svector_ostream name(nameBuf);

   if (!parsed.ContextName.empty())
      name << parsed.ContextName << '.';

   if (parsed.IsFunctionName) {
      name << parsed.formDeclName(ctx);
   } else {
      name << parsed.BaseName;
   }

   if (parsed.isMember() && parsed.isPropertyAccessor())
      return ReplacementDeclKind::Property;
   if (parsed.isInstanceMember() && parsed.IsFunctionName)
      return ReplacementDeclKind::InstanceMethod;

   // We don't have enough information.
   return ReplacementDeclKind::None;
}

/// Returns a value that can be used to select between accessor kinds in
/// diagnostics.
///
/// This is correlated with diag::availability_deprecated and others.
static std::pair<unsigned, DeclName>
getAccessorKindAndNameForDiagnostics(const ValueDecl *D) {
   // This should always be one more than the last AccessorKind supported in
   // the diagnostics. If you need to change it, change the assertion below as
   // well.
   static const unsigned NOT_ACCESSOR_INDEX = 2;

   if (auto *accessor = dyn_cast<AccessorDecl>(D)) {
      DeclName Name = accessor->getStorage()->getFullName();
      assert(accessor->isGetterOrSetter());
      return {static_cast<unsigned>(accessor->getAccessorKind()), Name};
   }

   return {NOT_ACCESSOR_INDEX, D->getFullName()};
}

void TypeChecker::diagnoseIfDeprecated(SourceRange ReferenceRange,
                                       const DeclContext *ReferenceDC,
                                       const ValueDecl *DeprecatedDecl,
                                       const ApplyExpr *Call) {
   const AvailableAttr *Attr = TypeChecker::getDeprecated(DeprecatedDecl);
   if (!Attr)
      return;

   // We don't want deprecated declarations to trigger warnings
   // in synthesized code.
   if (ReferenceRange.isInvalid() &&
       isInsideImplicitFunction(ReferenceRange, ReferenceDC)) {
      return;
   }
   // We match the behavior of clang to not report deprecation warnings
   // inside declarations that are themselves deprecated on all deployment
   // targets.
   if (isInsideDeprecatedDeclaration(ReferenceRange, ReferenceDC)) {
      return;
   }

   auto &Context = ReferenceDC->getAstContext();
   if (!Context.LangOpts.DisableAvailabilityChecking) {
      AvailabilityContext RunningOSVersions =
         overApproximateAvailabilityAtLocation(ReferenceRange.start,ReferenceDC);
      if (RunningOSVersions.isKnownUnreachable()) {
         // Suppress a deprecation warning if the availability checking machinery
         // thinks the reference program location will not execute on any
         // deployment target for the current platform.
         return;
      }
   }

   DeclName Name;
   unsigned RawAccessorKind;
   std::tie(RawAccessorKind, Name) =
      getAccessorKindAndNameForDiagnostics(DeprecatedDecl);

   StringRef Platform = Attr->prettyPlatformString();
   llvm::VersionTuple DeprecatedVersion;
   if (Attr->Deprecated)
      DeprecatedVersion = Attr->Deprecated.getValue();

   if (Attr->Message.empty() && Attr->Rename.empty()) {
      Context.Diags.diagnose(
            ReferenceRange.start, diag::availability_deprecated,
            RawAccessorKind, Name, Attr->hasPlatform(), Platform,
            Attr->Deprecated.hasValue(), DeprecatedVersion,
            /*message*/ StringRef())
         .highlight(Attr->getRange());
      return;
   }

   SmallString<32> newNameBuf;
   Optional<ReplacementDeclKind> replacementDeclKind =
      describeRename(Context, Attr, /*decl*/nullptr, newNameBuf);
   StringRef newName = replacementDeclKind ? newNameBuf.str() : Attr->Rename;

   if (!Attr->Message.empty()) {
      EncodedDiagnosticMessage EncodedMessage(Attr->Message);
      Context.Diags.diagnose(
            ReferenceRange.start, diag::availability_deprecated,
            RawAccessorKind, Name, Attr->hasPlatform(), Platform,
            Attr->Deprecated.hasValue(), DeprecatedVersion,
            EncodedMessage.Message)
         .highlight(Attr->getRange());
   } else {
      unsigned rawReplaceKind = static_cast<unsigned>(
         replacementDeclKind.getValueOr(ReplacementDeclKind::None));
      Context.Diags.diagnose(
            ReferenceRange.start, diag::availability_deprecated_rename,
            RawAccessorKind, Name, Attr->hasPlatform(), Platform,
            Attr->Deprecated.hasValue(), DeprecatedVersion,
            replacementDeclKind.hasValue(), rawReplaceKind, newName)
         .highlight(Attr->getRange());
   }

   if (!Attr->Rename.empty() && !isa<AccessorDecl>(DeprecatedDecl)) {
      auto renameDiag = Context.Diags.diagnose(
         ReferenceRange.start,
         diag::note_deprecated_rename,
         newName);
      fixItAvailableAttrRename(renameDiag, ReferenceRange, DeprecatedDecl,
                               Attr, Call);
   }
}


void polar::diagnoseUnavailableOverride(ValueDecl *override,
                                        const ValueDecl *base,
                                        const AvailableAttr *attr) {
   AstContext &ctx = override->getAstContext();
   auto &diags = ctx.Diags;
   if (attr->Rename.empty()) {
      EncodedDiagnosticMessage EncodedMessage(attr->Message);
      diags.diagnose(override, diag::override_unavailable,
                     override->getBaseName(), EncodedMessage.Message);

      DeclName name;
      unsigned rawAccessorKind;
      std::tie(rawAccessorKind, name) =
         getAccessorKindAndNameForDiagnostics(base);
      diags.diagnose(base, diag::availability_marked_unavailable,
                     rawAccessorKind, name);
      return;
   }

   diagnoseExplicitUnavailability(base, override->getLoc(),
                                  override->getDeclContext(),
                                  [&](InFlightDiagnostic &diag) {
                                     ParsedDeclName parsedName = parse_decl_name(attr->Rename);
                                     if (!parsedName || parsedName.isPropertyAccessor() ||
                                         parsedName.isMember() || parsedName.isOperator()) {
                                        return;
                                     }

                                     // Only initializers should be named 'init'.
                                     if (isa<ConstructorDecl>(override) ^
                                         (parsedName.BaseName == "init")) {
                                        return;
                                     }

                                     if (!parsedName.IsFunctionName) {
                                        diag.fixItReplace(override->getNameLoc(), parsedName.BaseName);
                                        return;
                                     }

                                     DeclName newName = parsedName.formDeclName(ctx);
                                     size_t numArgs = override->getFullName().getArgumentNames().size();
                                     if (!newName || newName.getArgumentNames().size() != numArgs)
                                        return;
                                     // @todo
//                                     fixDeclarationName(diag, override, newName);
                                  });
}

/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool polar::diagnoseExplicitUnavailability(const ValueDecl *D,
                                           SourceRange R,
                                           const DeclContext *DC,
                                           const ApplyExpr *call) {
   return diagnoseExplicitUnavailability(D, R, DC,
                                         [=](InFlightDiagnostic &diag) {
                                            fixItAvailableAttrRename(diag, R, D, AvailableAttr::isUnavailable(D),
                                                                     call);
                                         });
}

/// Check if this is a subscript declaration inside String or
/// Substring that returns String, and if so return true.
bool isSubscriptReturningString(const ValueDecl *D, AstContext &Context) {
   // Is this a subscript?
   if (!isa<SubscriptDecl>(D))
      return false;

   // Is the subscript declared in String or Substring?
   auto *declContext = D->getDeclContext();
   assert(declContext && "Expected decl context!");

   auto *stringDecl = Context.getStringDecl();
   auto *substringDecl = Context.getSubstringDecl();

   auto *typeDecl = declContext->getSelfNominalTypeDecl();
   if (!typeDecl)
      return false;

   if (typeDecl != stringDecl && typeDecl != substringDecl)
      return false;

   // Is the subscript index one we want to emit a special diagnostic
   // for, and the return type String?
   auto fnTy = D->getInterfaceType()->getAs<AnyFunctionType>();
   assert(fnTy && "Expected function type for subscript decl!");

   // We're only going to warn for BoundGenericStructType with a single
   // type argument that is not Int!
   auto params = fnTy->getParams();
   if (params.size() != 1)
      return false;

   const auto &param = params.front();
   if (param.hasLabel() || param.isVariadic() || param.isInOut())
      return false;

   auto inputTy = param.getPlainType()->getAs<BoundGenericStructType>();
   if (!inputTy)
      return false;

   auto genericArgs = inputTy->getGenericArgs();
   if (genericArgs.size() != 1)
      return false;

   // The subscripts taking T<Int> do not return Substring, and our
   // special fixit does not help here.
   auto intDecl = Context.getIntDecl();
   auto nominalTypeParam = genericArgs[0]->getAs<NominalType>();
   if (!nominalTypeParam)
      return false;

   if (nominalTypeParam->getDecl() == intDecl)
      return false;

   auto resultTy = fnTy->getResult()->getAs<NominalType>();
   if (!resultTy)
      return false;

   return resultTy->getDecl() == stringDecl;
}

bool polar::diagnoseExplicitUnavailability(
   const ValueDecl *D,
   SourceRange R,
   const DeclContext *DC,
   llvm::function_ref<void(InFlightDiagnostic &)> attachRenameFixIts) {
   auto *Attr = AvailableAttr::isUnavailable(D);
   if (!Attr)
      return false;

   // Suppress the diagnostic if we are in synthesized code inside
   // a synthesized function and the reference is lexically
   // contained in a declaration that is itself marked unavailable.
   // The right thing to do here is to not synthesize that code in the
   // first place. rdar://problem/20491640
   if (R.isInvalid() && isInsideImplicitFunction(R, DC) &&
       isInsideUnavailableDeclaration(R, DC)) {
      return false;
   }

   // Calling unavailable code from within code with the same
   // unavailability is OK -- the eventual caller can't call the
   // enclosing code in the same situations it wouldn't be able to
   // call this code.
   if (isInsideCompatibleUnavailableDeclaration(D, R, DC, Attr)) {
      return false;
   }

   SourceLoc Loc = R.start;
   DeclName Name;
   unsigned RawAccessorKind;
   std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);

   AstContext &ctx = D->getAstContext();
   auto &diags = ctx.Diags;

   StringRef platform;
   switch (Attr->getPlatformAgnosticAvailability()) {
      case PlatformAgnosticAvailabilityKind::Deprecated:
         llvm_unreachable("shouldn't see deprecations in explicit unavailability");

      case PlatformAgnosticAvailabilityKind::None:
      case PlatformAgnosticAvailabilityKind::Unavailable:
         if (Attr->Platform != PlatformKind::none) {
            // This was platform-specific; indicate the platform.
            platform = Attr->prettyPlatformString();
            break;
         }
         LLVM_FALLTHROUGH;

      case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
      case PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific:
         // We don't want to give further detail about these.
         platform = "";
         break;

      case PlatformAgnosticAvailabilityKind::UnavailableInSwift:
         // This API is explicitly unavailable in Swift.
         platform = "Swift";
         break;
   }

   if (!Attr->Rename.empty()) {
      SmallString<32> newNameBuf;
      Optional<ReplacementDeclKind> replaceKind =
         describeRename(ctx, Attr, D, newNameBuf);
      unsigned rawReplaceKind = static_cast<unsigned>(
         replaceKind.getValueOr(ReplacementDeclKind::None));
      StringRef newName = replaceKind ? newNameBuf.str() : Attr->Rename;
      EncodedDiagnosticMessage EncodedMessage(Attr->Message);
      auto diag =
         diags.diagnose(Loc, diag::availability_decl_unavailable_rename,
                        RawAccessorKind, Name, replaceKind.hasValue(),
                        rawReplaceKind, newName, EncodedMessage.Message);
      attachRenameFixIts(diag);
   } else if (isSubscriptReturningString(D, ctx)) {
      diags.diagnose(Loc, diag::availabilty_string_subscript_migration)
         .highlight(R)
         .fixItInsert(R.start, "String(")
         .fixItInsertAfter(R.end, ")");

      // Skip the note emitted below.
      return true;
   } else {
      EncodedDiagnosticMessage EncodedMessage(Attr->Message);
      diags
         .diagnose(Loc, diag::availability_decl_unavailable, RawAccessorKind,
                   Name, platform.empty(), platform, EncodedMessage.Message)
         .highlight(R);
   }

   switch (Attr->getVersionAvailability(ctx)) {
      case AvailableVersionComparison::Available:
      case AvailableVersionComparison::PotentiallyUnavailable:
         llvm_unreachable("These aren't considered unavailable");

      case AvailableVersionComparison::Unavailable:
         if ((Attr->isLanguageVersionSpecific() ||
              Attr->isPackageDescriptionVersionSpecific())
             && Attr->Introduced.hasValue())
            diags.diagnose(D, diag::availability_introduced_in_version,
                           RawAccessorKind, Name,
                           (Attr->isLanguageVersionSpecific() ?
                            "Swift" : "PackageDescription"),
                           *Attr->Introduced)
               .highlight(Attr->getRange());
         else
            diags.diagnose(D, diag::availability_marked_unavailable, RawAccessorKind,
                           Name)
               .highlight(Attr->getRange());
         break;

      case AvailableVersionComparison::Obsoleted:
         // FIXME: Use of the platformString here is non-awesome for application
         // extensions.

         StringRef platformDisplayString;
         if (Attr->isLanguageVersionSpecific()) {
            platformDisplayString = "Swift";
         } else if (Attr->isPackageDescriptionVersionSpecific()) {
            platformDisplayString = "PackageDescription";
         } else {
            platformDisplayString = platform;
         }

         diags.diagnose(D, diag::availability_obsoleted,
                        RawAccessorKind, Name,
                        platformDisplayString,
                        *Attr->Obsoleted)
            .highlight(Attr->getRange());
         break;
   }
   return true;
}

namespace {
class AvailabilityWalker : public AstWalker {
   /// Describes how the next member reference will be treated as we traverse
   /// the AST.
   enum class MemberAccessContext : unsigned {
      /// The member reference is in a context where an access will call
      /// the getter.
         Getter,

      /// The member reference is in a context where an access will call
      /// the setter.
         Setter,

      /// The member reference is in a context where it will be turned into
      /// an inout argument. (Once this happens, we have to conservatively assume
      /// that both the getter and setter could be called.)
         InOut
   };

   AstContext &Context;
   DeclContext *DC;
   MemberAccessContext AccessContext = MemberAccessContext::Getter;
   SmallVector<const Expr *, 16> ExprStack;
   ResilienceExpansion Expansion;
   Optional<TypeChecker::FragileFunctionKind> FragileKind;
   bool TreatUsableFromInlineAsPublic = false;

   /// Returns true if DC is an \c init(rawValue:) declaration and it is marked
   /// implicit.
   bool inSynthesizedInitRawValue() {
      auto init = dyn_cast_or_null<ConstructorDecl>(
         DC->getInnermostDeclarationDeclContext());

      return init &&
             init->isImplicit() &&
             init->getParameters()->size() == 1 &&
             init->getParameters()->get(0)->getArgumentName() ==
             Context.Id_rawValue;
   }

public:
   AvailabilityWalker(DeclContext *DC) : Context(DC->getAstContext()), DC(DC) {
      Expansion = DC->getResilienceExpansion();
      if (Expansion == ResilienceExpansion::Minimal)
         std::tie(FragileKind, TreatUsableFromInlineAsPublic)
            = TypeChecker::getFragileFunctionKind(DC);
   }

   // FIXME: Remove this
   bool shouldWalkAccessorsTheOldWay() override {
      return true;
   }

   bool shouldWalkIntoNonSingleExpressionClosure() override {
      return false;
   }

   std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
      ExprStack.push_back(E);

      auto visitChildren = [&]() { return std::make_pair(true, E); };
      auto skipChildren = [&]() {
         ExprStack.pop_back();
         return std::make_pair(false, E);
      };

      if (auto DR = dyn_cast<DeclRefExpr>(E)) {
         DeclAvailabilityFlags flags = None;
         if (inSynthesizedInitRawValue())
            // HACK: If a raw-value enum has cases with `@available(introduced:)`
            // attributes, the auto-derived `init(rawValue:)` will contain
            // DeclRefExprs which reference those cases. It will also contain
            // appropriate `guard #available` statements to keep them from running
            // on older versions, but the availability checker can't verify that
            // because the synthesized code uses invalid SourceLocs. Don't diagnose
            // these errors; instead, take it on faith that
            // DerivedConformanceRawRepresentable will do the right thing.
            flags |= DeclAvailabilityFlag::AllowPotentiallyUnavailable;

         diagAvailability(DR->getDeclRef(), DR->getSourceRange(),
                          getEnclosingApplyExpr(), flags);
         maybeDiagStorageAccess(DR->getDecl(), DR->getSourceRange(), DC);
      }
      if (auto MR = dyn_cast<MemberRefExpr>(E)) {
         walkMemberRef(MR);
         return skipChildren();
      }
      if (auto OCDR = dyn_cast<OtherConstructorDeclRefExpr>(E))
         diagAvailability(OCDR->getDeclRef(),
                          OCDR->getConstructorLoc().getSourceRange(),
                          getEnclosingApplyExpr());
      if (auto DMR = dyn_cast<DynamicMemberRefExpr>(E))
         diagAvailability(DMR->getMember(),
                          DMR->getNameLoc().getSourceRange(),
                          getEnclosingApplyExpr());
      if (auto DS = dyn_cast<DynamicSubscriptExpr>(E))
         diagAvailability(DS->getMember(), DS->getSourceRange());
      if (auto S = dyn_cast<SubscriptExpr>(E)) {
         if (S->hasDecl()) {
            diagAvailability(S->getDecl(), S->getSourceRange());
            maybeDiagStorageAccess(S->getDecl().getDecl(), S->getSourceRange(), DC);
         }
      }
      if (auto KP = dyn_cast<KeyPathExpr>(E)) {
         maybeDiagKeyPath(KP);
      }
      if (auto A = dyn_cast<AssignExpr>(E)) {
         walkAssignExpr(A);
         return skipChildren();
      }
      if (auto IO = dyn_cast<InOutExpr>(E)) {
         walkInOutExpr(IO);
         return skipChildren();
      }

      return visitChildren();
   }

   Expr *walkToExprPost(Expr *E) override {
      assert(ExprStack.back() == E);
      ExprStack.pop_back();

      return E;
   }

   bool diagAvailability(ConcreteDeclRef declRef, SourceRange R,
                         const ApplyExpr *call = nullptr,
                         DeclAvailabilityFlags flags = None) const;

private:
   bool diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R,
                              const AvailableAttr *Attr) const;
   bool diagnoseMemoryLayoutMigration(const ValueDecl *D, SourceRange R,
                                      const AvailableAttr *Attr,
                                      const ApplyExpr *call) const;

   /// Walks up from a potential callee to the enclosing ApplyExpr.
   const ApplyExpr *getEnclosingApplyExpr() const {
      ArrayRef<const Expr *> parents = ExprStack;
      assert(!parents.empty() && "must be called while visiting an expression");
      size_t idx = parents.size() - 1;

      do {
         if (idx == 0)
            return nullptr;
         --idx;
      } while (isa<DotSyntaxBaseIgnoredExpr>(parents[idx]) || // Mod.f(a)
               isa<SelfApplyExpr>(parents[idx]) || // obj.f(a)
               isa<IdentityExpr>(parents[idx]) || // (f)(a)
               isa<ForceValueExpr>(parents[idx]) || // f!(a)
               isa<BindOptionalExpr>(parents[idx])); // f?(a)

      auto *call = dyn_cast<ApplyExpr>(parents[idx]);
      if (!call || call->getFn() != parents[idx+1])
         return nullptr;
      return call;
   }

   /// Walk an assignment expression, checking for availability.
   void walkAssignExpr(AssignExpr *E) {
      // We take over recursive walking of assignment expressions in order to
      // walk the destination and source expressions in different member
      // access contexts.
      Expr *Dest = E->getDest();
      if (!Dest) {
         return;
      }

      // Check the Dest expression in a setter context.
      // We have an implicit assumption here that the first MemberRefExpr
      // encountered walking (pre-order) is the Dest is the destination of the
      // write. For the moment this is fine -- but future syntax might violate
      // this assumption.
      walkInContext(E, Dest, MemberAccessContext::Setter);

      // Check RHS in getter context
      Expr *Source = E->getSrc();
      if (!Source) {
         return;
      }
      walkInContext(E, Source, MemberAccessContext::Getter);
   }

   /// Walk a member reference expression, checking for availability.
   void walkMemberRef(MemberRefExpr *E) {
      // Walk the base in a getter context.
      // FIXME: We may need to look at the setter too, if we're going to do
      // writeback. The AST should have this information.
      walkInContext(E, E->getBase(), MemberAccessContext::Getter);

      ValueDecl *D = E->getMember().getDecl();
      // Diagnose for the member declaration itself.
      if (diagAvailability(D, E->getNameLoc().getSourceRange()))
         return;

      // Diagnose for appropriate accessors, given the access context.
      maybeDiagStorageAccess(D, E->getSourceRange(), DC);
   }

   /// Walk a keypath expression, checking all of its components for
   /// availability.
   void maybeDiagKeyPath(KeyPathExpr *KP) {
      for (auto &component : KP->getComponents()) {
         switch (component.getKind()) {
            case KeyPathExpr::Component::Kind::Property:
            case KeyPathExpr::Component::Kind::Subscript: {
               auto *decl = component.getDeclRef().getDecl();
               auto loc = component.getLoc();
               SourceRange range(loc, loc);
               diagAvailability(decl, range, nullptr);
               break;
            }

            case KeyPathExpr::Component::Kind::TupleElement:
               break;

            case KeyPathExpr::Component::Kind::Invalid:
            case KeyPathExpr::Component::Kind::UnresolvedProperty:
            case KeyPathExpr::Component::Kind::UnresolvedSubscript:
            case KeyPathExpr::Component::Kind::OptionalChain:
            case KeyPathExpr::Component::Kind::OptionalWrap:
            case KeyPathExpr::Component::Kind::OptionalForce:
            case KeyPathExpr::Component::Kind::Identity:
               break;
         }
      }
   }

   /// Walk an inout expression, checking for availability.
   void walkInOutExpr(InOutExpr *E) {
      walkInContext(E, E->getSubExpr(), MemberAccessContext::InOut);
   }

   /// Walk the given expression in the member access context.
   void walkInContext(Expr *baseExpr, Expr *E,
                      MemberAccessContext AccessContext) {
      llvm::SaveAndRestore<MemberAccessContext>
         C(this->AccessContext, AccessContext);
      E->walk(*this);
   }

   /// Emit diagnostics, if necessary, for accesses to storage where
   /// the accessor for the AccessContext is not available.
   void maybeDiagStorageAccess(const ValueDecl *VD,
                               SourceRange ReferenceRange,
                               const DeclContext *ReferenceDC) const {
      if (Context.LangOpts.DisableAvailabilityChecking)
         return;

      auto *D = dyn_cast<AbstractStorageDecl>(VD);
      if (!D)
         return;

      if (!D->requiresOpaqueAccessors()) {
         return;
      }

      // Check availability of accessor functions.
      // TODO: if we're talking about an inlineable storage declaration,
      // this probably needs to be refined to not assume that the accesses are
      // specifically using the getter/setter.
      switch (AccessContext) {
         case MemberAccessContext::Getter:
            diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Get),
                                     ReferenceRange, ReferenceDC, None);
            break;

         case MemberAccessContext::Setter:
            diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Set),
                                     ReferenceRange, ReferenceDC, None);
            break;

         case MemberAccessContext::InOut:
            diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Get),
                                     ReferenceRange, ReferenceDC,
                                     DeclAvailabilityFlag::ForInout);

            diagAccessorAvailability(D->getOpaqueAccessor(AccessorKind::Set),
                                     ReferenceRange, ReferenceDC,
                                     DeclAvailabilityFlag::ForInout);
            break;
      }
   }

   /// Emit a diagnostic, if necessary for a potentially unavailable accessor.
   void diagAccessorAvailability(AccessorDecl *D, SourceRange ReferenceRange,
                                 const DeclContext *ReferenceDC,
                                 DeclAvailabilityFlags Flags) const {
      Flags &= DeclAvailabilityFlag::ForInout;
      Flags |= DeclAvailabilityFlag::ContinueOnPotentialUnavailability;
      if (diagAvailability(D, ReferenceRange, /*call*/nullptr, Flags))
         return;
   }
};
} // end anonymous namespace

/// Diagnose uses of unavailable declarations. Returns true if a diagnostic
/// was emitted.
bool
AvailabilityWalker::diagAvailability(ConcreteDeclRef declRef, SourceRange R,
                                     const ApplyExpr *call,
                                     DeclAvailabilityFlags Flags) const {
   if (!declRef)
      return false;
   const ValueDecl *D = declRef.getDecl();

   if (auto *attr = AvailableAttr::isUnavailable(D)) {
      if (diagnoseIncDecRemoval(D, R, attr))
         return true;
      if (call && diagnoseMemoryLayoutMigration(D, R, attr, call))
         return true;
   }

   // Keep track if this is an accessor.
   auto accessor = dyn_cast<AccessorDecl>(D);

   if (accessor) {
      // If the property/subscript is unconditionally unavailable, don't bother
      // with any of the rest of this.
      if (AvailableAttr::isUnavailable(accessor->getStorage()))
         return false;
   }

   if (FragileKind)
      if (R.isValid())
         if (TypeChecker::diagnoseInlinableDeclRef(R.start, declRef, DC, *FragileKind,
                                                   TreatUsableFromInlineAsPublic))
            return true;

   if (diagnoseExplicitUnavailability(D, R, DC, call))
      return true;

   // Make sure not to diagnose an accessor's deprecation if we already
   // complained about the property/subscript.
   bool isAccessorWithDeprecatedStorage =
      accessor && TypeChecker::getDeprecated(accessor->getStorage());

   // Diagnose for deprecation
   if (!isAccessorWithDeprecatedStorage)
      TypeChecker::diagnoseIfDeprecated(R, DC, D, call);

   if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailable))
      return false;

   if (Flags.contains(DeclAvailabilityFlag::AllowPotentiallyUnavailableInterface)
       && isa<InterfaceDecl>(D))
      return false;

   // Diagnose (and possibly signal) for potential unavailability
   auto maybeUnavail = TypeChecker::checkDeclarationAvailability(D, R.start, DC);
   if (maybeUnavail.hasValue()) {
      if (accessor) {
         bool forInout = Flags.contains(DeclAvailabilityFlag::ForInout);
         TypeChecker::diagnosePotentialAccessorUnavailability(accessor, R, DC,
                                                              maybeUnavail.getValue(),
                                                              forInout);
      } else {
         TypeChecker::diagnosePotentialUnavailability(D, R, DC, maybeUnavail.getValue());
      }
      if (!Flags.contains(DeclAvailabilityFlag::ContinueOnPotentialUnavailability))
         return true;
   }
   return false;
}


/// Return true if the specified type looks like an integer of floating point
/// type.
static bool isIntegerOrFloatingPointType(Type ty, DeclContext *DC,
                                         AstContext &Context) {
   auto integerType =
      Context.getInterface(KnownInterfaceKind::ExpressibleByIntegerLiteral);
   auto floatingType =
      Context.getInterface(KnownInterfaceKind::ExpressibleByFloatLiteral);
   if (!integerType || !floatingType) return false;

   return TypeChecker::conformsToInterface(ty, integerType, DC,
                                          ConformanceCheckFlags::InExpression) ||
          TypeChecker::conformsToInterface(ty, floatingType, DC,
                                          ConformanceCheckFlags::InExpression);
}


/// If this is a call to an unavailable ++ / -- operator, try to diagnose it
/// with a fixit hint and return true.  If not, or if we fail, return false.
bool
AvailabilityWalker::diagnoseIncDecRemoval(const ValueDecl *D, SourceRange R,
                                          const AvailableAttr *Attr) const {
   // We can only produce a fixit if we're talking about ++ or --.
   bool isInc = D->getBaseName() == "++";
   if (!isInc && D->getBaseName() != "--")
      return false;

   // We can only handle the simple cases of lvalue++ and ++lvalue.  This is
   // always modeled as:
   //   (postfix_unary_expr (declrefexpr ++), (inoutexpr (lvalue)))
   // if not, bail out.
   if (ExprStack.size() != 2 ||
       !isa<DeclRefExpr>(ExprStack[1]) ||
       !(isa<PostfixUnaryExpr>(ExprStack[0]) ||
         isa<PrefixUnaryExpr>(ExprStack[0])))
      return false;

   auto call = cast<ApplyExpr>(ExprStack[0]);

   // If the expression type is integer or floating point, then we can rewrite it
   // to "lvalue += 1".
   std::string replacement;
   if (isIntegerOrFloatingPointType(call->getType(), DC, Context))
      replacement = isInc ? " += 1" : " -= 1";
   else {
      // Otherwise, it must be an index type.  Rewrite to:
      // "lvalue = lvalue.successor()".
      auto &SM = Context.SourceMgr;
      auto CSR = Lexer::getCharSourceRangeFromSourceRange(SM,
                                                          call->getArg()->getSourceRange());
      replacement = " = " + SM.extractText(CSR).str();
      replacement += isInc ? ".successor()" : ".predecessor()";
   }

   if (!replacement.empty()) {
      DeclName Name;
      unsigned RawAccessorKind;
      std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);

      // If we emit a deprecation diagnostic, produce a fixit hint as well.
      auto diag = Context.Diags.diagnose(
         R.start, diag::availability_decl_unavailable,
         RawAccessorKind, Name, true, "",
         "it has been removed in Swift 3");
      if (isa<PrefixUnaryExpr>(call)) {
         // Prefix: remove the ++ or --.
         diag.fixItRemove(call->getFn()->getSourceRange());
         diag.fixItInsertAfter(call->getArg()->getEndLoc(), replacement);
      } else {
         // Postfix: replace the ++ or --.
         diag.fixItReplace(call->getFn()->getSourceRange(), replacement);
      }

      return true;
   }


   return false;
}

/// If this is a call to an unavailable sizeof family function, diagnose it
/// with a fixit hint and return true. If not, or if we fail, return false.
bool
AvailabilityWalker::diagnoseMemoryLayoutMigration(const ValueDecl *D,
                                                  SourceRange R,
                                                  const AvailableAttr *Attr,
                                                  const ApplyExpr *call) const {

   if (!D->getModuleContext()->isStdlibModule())
      return false;

   StringRef Property;
   if (D->getBaseName() == "sizeof") {
      Property = "size";
   } else if (D->getBaseName() == "alignof") {
      Property = "alignment";
   } else if (D->getBaseName() == "strideof") {
      Property = "stride";
   }

   if (Property.empty())
      return false;

   auto args = dyn_cast<ParenExpr>(call->getArg());
   if (!args)
      return false;

   DeclName Name;
   unsigned RawAccessorKind;
   std::tie(RawAccessorKind, Name) = getAccessorKindAndNameForDiagnostics(D);

   EncodedDiagnosticMessage EncodedMessage(Attr->Message);
   auto diag =
      Context.Diags.diagnose(
         R.start, diag::availability_decl_unavailable, RawAccessorKind,
         Name, true, "", EncodedMessage.Message);
   diag.highlight(R);

   auto subject = args->getSubExpr();

   StringRef Prefix = "MemoryLayout<";
   StringRef Suffix = ">.";

   if (auto DTE = dyn_cast<DynamicTypeExpr>(subject)) {
      // Replace `sizeof(type(of: x))` with `MemoryLayout<X>.size`, where `X` is
      // the static type of `x`. The previous spelling misleadingly hinted that
      // `sizeof(_:)` might return the size of the *dynamic* type of `x`, when
      // it is not the case.
      auto valueType = DTE->getBase()->getType()->getRValueType();
      if (!valueType || valueType->hasError()) {
         // If we don't have a suitable argument, we can't emit a fixit.
         return true;
      }
      // Note that in rare circumstances we may be destructively replacing the
      // source text. For example, we'd replace `sizeof(type(of: doSomething()))`
      // with `MemoryLayout<T>.size`, if T is the return type of `doSomething()`.
      diag.fixItReplace(call->getSourceRange(),
                        (Prefix + valueType->getString() + Suffix + Property).str());
   } else {
      SourceRange PrefixRange(call->getStartLoc(), args->getLParenLoc());
      SourceRange SuffixRange(args->getRParenLoc());

      // We must remove `.self`.
      if (auto *DSE = dyn_cast<DotSelfExpr>(subject))
         SuffixRange.start = DSE->getDotLoc();

      diag
         .fixItReplace(PrefixRange, Prefix)
         .fixItReplace(SuffixRange, (Suffix + Property).str());
   }

   return true;
}

/// Diagnose uses of unavailable declarations.
void polar::diagAvailability(const Expr *E, DeclContext *DC) {
   AvailabilityWalker walker(DC);
   const_cast<Expr*>(E)->walk(walker);
}

/// Run the Availability-diagnostics algorithm otherwise used in an expr
/// context, but for non-expr contexts such as TypeDecls referenced from
/// TypeReprs.
bool polar::diagnoseDeclAvailability(const ValueDecl *Decl,
                                     DeclContext *DC,
                                     SourceRange R,
                                     DeclAvailabilityFlags Flags)
{
   AvailabilityWalker AW(DC);
   return AW.diagAvailability(const_cast<ValueDecl *>(Decl), R, nullptr, Flags);
}

void polar::checkExplicitAvailability(Decl *decl) {
   // Check only if the command line option was set.
   if (!decl->getAstContext().LangOpts.RequireExplicitAvailability)
      return;

   // Skip nominal type members as the type should be annotated.
   auto declContext = decl->getDeclContext();
   if (isa<NominalTypeDecl>(declContext))
      return;

   ValueDecl *valueDecl = dyn_cast<ValueDecl>(decl);
   if (valueDecl == nullptr) {
      // decl should be either a ValueDecl or an ExtensionDecl
      auto extension = cast<ExtensionDecl>(decl);
      valueDecl = extension->getExtendedNominal();
      if (!valueDecl)
         return;
   }

   // Skip decls that are not public and not usable from inline.
   AccessScope scope =
      valueDecl->getFormalAccessScope(/*useDC*/nullptr,
         /*treatUsableFromInlineAsPublic*/true);
   if (!scope.isPublic() ||
       decl->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>())
      return;

   // Warn on decls without an introduction version.
   auto &ctx = decl->getAstContext();
   auto safeRangeUnderApprox = AvailabilityInference::availableRange(decl, ctx);
   if (!safeRangeUnderApprox.getOSVersion().hasLowerEndpoint() &&
       !decl->getAttrs().isUnavailable(ctx)) {

      auto diag = decl->diagnose(diag::public_decl_needs_availability);

      auto suggestPlatform = decl->getAstContext().LangOpts.RequireExplicitAvailabilityTarget;
      if (!suggestPlatform.empty()) {
         auto InsertLoc = decl->getAttrs().getStartLoc(/*forModifiers=*/false);
         if (InsertLoc.isInvalid())
            InsertLoc = decl->getStartLoc();

         if (InsertLoc.isInvalid())
            return;

         std::string AttrText;
         {
            llvm::raw_string_ostream Out(AttrText);

            StringRef OriginalIndent = Lexer::getIndentationForLine(
               ctx.SourceMgr, InsertLoc);
            Out << "@available(" << suggestPlatform << ", *)\n"
                << OriginalIndent;
         }

         diag.fixItInsert(InsertLoc, AttrText);
      }
   }
}
